#ifndef MONO_COMMON_PATH_H_
#define MONO_COMMON_PATH_H_
#include <string>
#include <vector>
#include <cstddef>

namespace mono {

/*!
* This class represents filesystem paths in a 
* platform-independent manner.
* Unix, Windows and OpenVMS all use a different
* syntax for filesystem paths.
* This class can work with all three formats.
* A path is made up of an optional node name
* (only Windows and OpenVMS), an optional
* device name (also only Windows and OpenVMS),
* a list of directory names and an optional
* filename.
*/

class  Path{
public:
	typedef std::vector<std::string> StringVec;

	/*!
	* Creates an empty relative path.
	*/	
	Path();
		
	/*!
	* Creates an empty absolute or relative path.
	*/
	Path(bool absolute);
		
	/*!
	* Creates a path from a string.
	*/
	Path(const char* path);
		
	/*!
	* Creates a path from a string.
	*/
	Path(const std::string& path);

	/*!
	* Copy constructor
	*/
	Path(const Path& path);

	/*!
	* Creates a path from a parent path and a filename.
	* The parent path is expected to reference a directory.
	*/
	Path(const Path& parent, const std::string& fileName);
		

	/*!
	* Creates a path from a parent path and a filename.
	* The parent path is expected to reference a directory.
	*/
	Path(const Path& parent, const char* fileName);
		
	/*!
	* Creates a path from a parent path and a relative path.
	* The parent path is expected to reference a directory.
	* The relative path is appended to the parent path.
	*/
	Path(const Path& parent, const Path& relative);


	/*!
	* Destroys the Path.
	*/
	~Path();
		
	/*!	
	* Assignment operator.
	*/
	Path& operator = (const Path& path);
		
	/*!	
	* Assigns a string containing a path in native format.
	*/	
	Path& operator = (const std::string& path);
		
	/*!	
	*Assigns a string containing a path in native format.
	*/
	Path& operator = (const char* path);
		
	/*!	
	* Swaps the path with another one.
	*/
	void swap(Path& path);	

	/*!	
	* Assigns a string containing a path in native format.
	*/
	Path& assign(const std::string& path);
		
	/*!	
	* Assigns the given path.
	*/	
	Path& assign(const Path& path);
		
	/*!	
	* Assigns a string containing a path.
	*/
	Path& assign(const char* path);
		
	/*!	
	*@return a string containing the path in native format.	
	*/
	std::string toString() const;

	/*!	
	*@see Same as assign().
	*/
	void parse(const std::string& path);
	
	/*!	
	* Tries to interpret the given string as a path
	* in native format.
	* If the path is syntactically valid, assigns the
	* path and returns true. Otherwise leaves the 
	* object unchanged and returns false.
	*/
	bool tryParse(const std::string& path);


	/*!	
	* The resulting path always refers to a directory and
	* the filename part is empty.
	*/
	Path& parseDirectory(const std::string& path);
	
	/*!	
	*If the path contains a filename, the filename is appended
	* to the directory list and cleared. Thus the resulting path
	* always refers to a directory.
	*/
	Path& makeDirectory();

	/*!	
	* If the path contains no filename, the last directory
	* becomes the filename.
	*/
	Path& makeFile();

	/*!	
	* Makes the path refer to its parent.
	*/
	Path& makeParent();

	/*!	
	* Makes the path absolute if it is relative.
	* The current working directory is taken as base directory.
	*/	
	Path& makeAbsolute();
	
	/*!	
	* Makes the path absolute if it is relative.
	* The given path is taken as base. 
	*/
	Path& makeAbsolute(const Path& base);

	/*!	
	* Appends the given path.
	*/
	Path& append(const Path& path);
		
	/*!	
	* Resolves the given path agains the current one.
	* If the given path is absolute, it replaces the current one.
	* Otherwise, the relative path is appended to the current path.
	*/	
	Path& resolve(const Path& path);

	/*!	
	* @return true iff the path is valid.
	*/
	bool valid();
	/*!	
	* @return true iff the path is absolute.
	*/
	bool isAbsolute() const;

	/*!	
	*@eturn true iff the path is relative.
	*/
	bool isRelative() const;
		
	/*!	
	*@return true iff the path references a directory
	* (the filename part is empty).
	*/
	bool isDirectory() const;
	
	/*!	
	*@return true iff the path references a file
	* (the filename part is not empty).
	*/	
	bool isFile() const;

	/*!	
	* Sets the node name.
	* Setting a non-empty node automatically makes
	* the path an absolute one.
	*/
	Path& setNode(const std::string& node);

	/*!	
	*@return the node name.
	*/		
	const std::string& getNode() const;

	/*!	
	* Sets the device name.
	* Setting a non-empty device automatically makes
	* the path an absolute one.
	*/	
	Path& setDevice(const std::string& device);

	/*!	
	*@return the device name.
	*/	
	const std::string& getDevice() const;
		
	/*!	
	*@return the number of directories in the directory list.
	*/	
	int depth() const;

	/*!	
	*@return the n'th directory in the directory list.
	* If n == depth(), returns the filename.
	*/	
	const std::string& directory(size_t n) const;
	
	/*!	
	*@return the n'th directory in the directory list.
	* If n == depth(), returns the filename.
	*/		
	const std::string& operator [] (size_t n) const;
	
	/*!	
	*Adds a directory to the directory list.
	*/	
	Path& pushDirectory(const std::string& dir);

	/*!	
	*Removes the last directory from the directory list.
	*/	
	Path& popDirectory();
	
	/*!	
	*Removes the first directory from the directory list.
	*/		
	Path& popFrontDirectory();
	
	/*!	
	* Sets the filename.
	*/	
	Path& setFileName(const std::string& name);

	/*!	
	*@return the filename.
	*/		
	const std::string& getFileName() const;
		
	/*!	
	* Sets the basename part of the filename and
	* does not change the extension.
	*/	
	Path& setBaseName(const std::string& name);

	/*!	
	* @return the basename (the filename sans
	* extension) of the path.
	*/	
	std::string getBaseName() const;

	/*!	
	* Sets the filename extension.
	*/
	Path& setExtension(const std::string& extension);

	/*!	
	* @return the filename extension.
	*/
	std::string getExtension() const;
	
	/*!	
	* @return the file version, no use just empty.
	*/	
	const std::string& version() const;

	/*!	
	* Clears all
	*/		
	Path& clear();

	/*!	
	* @return a path referring to the path's directory.
	*/	
	Path parent() const;

	/*!	
	* @return an absolute variant of the path,
	* taking the current working directory as base.
	*/		
	Path absolute() const;
	
	/*!	
	* @return an absolute variant of the path,
	* taking the given path as base.
	*/	
	Path absolute(const Path& base) const;

	/*!	
	* Creates a path referring to a directory.
	*/	
	static Path forDirectory(const std::string& path);
		
	/*!	
	* @return '/' on unix like system.
	*/
	static char separator();

	/*!	
	* @return ':' on unix like system.
	*/
	static char pathSeparator();

	/*!	
	* @return the current working directory.
	*/
	static std::string current();

	/*!	
	* @return the user's home directory.
	*/
	static std::string home();
		
	/*!	
	* @return the temporary directory.
	*/
	static std::string temp();

	/*!	
	* @return the name of the null device.
	*/	
	static std::string null();

	/*!	
	* @return Expands all environment variables contained in the path.
	* On Unix, a tilde as first character in the path is
	* replaced with the path to user's home directory.
	*/		
	static std::string expand(const std::string& path);

	/*!	
	* @return "/"
	*/	
	static void listRoots(std::vector<std::string>& roots);

	/*!	
	* Searches the file with the given name in the locations (paths) specified
	* by it and end. A relative path may be given in name.
	*
	* If the file is found in one of the locations, the complete
	* path of the file is stored in the path given as argument and true is returned. 
	* Otherwise false is returned and the path argument remains unchanged.
	*/	
	static bool find(StringVec::const_iterator it, StringVec::const_iterator end, const std::string& name, Path& path);
	
	/*!	
	* Searches the file with the given name in the locations (paths) specified
	* in pathList. The paths in pathList must be delimited by the platform's
	* path separator (see pathSeparator()). A relative path may be given in name.
	*
	* If the file is found in one of the locations, the complete
	* path of the file is stored in the path given as argument and true is returned. 
	* Otherwise false is returned and the path argument remains unchanged.
	*/	
	static bool find(const std::string& pathList, const std::string& name, Path& path);

	/*!	
	* no change now 
	*/		
	static std::string transcode(const std::string& path);


protected:
	std::string build() const;


private:
	std::string _node;
	std::string _device;
	std::string _name;
	std::string _version;
	StringVec   _dirs;
	bool        _absolute;
};


//
// inlines
//
inline bool Path::isAbsolute() const
{
	return _absolute;
}

	
inline bool Path::isRelative() const
{
	return !_absolute;
}


inline bool Path::isDirectory() const
{
	return _name.empty();
}


inline bool Path::isFile() const
{
	return !_name.empty();
}


inline const std::string& Path::getNode() const
{
	return _node;
}


inline const std::string& Path::getDevice() const
{
	return _device;
}


inline const std::string& Path::getFileName() const
{
	return _name;
}


inline int Path::depth() const
{
	return int(_dirs.size());
}


inline const std::string& Path::version() const
{
	return _version;
}


inline Path Path::forDirectory(const std::string& path)
{
	Path p;
	return p.parseDirectory(path);
}
	

inline char Path::separator()
{
	return '/';
}


inline char Path::pathSeparator()
{
	return ':';
}


inline void swap(Path& p1, Path& p2)
{
	p1.swap(p2);
}


} 


#endif 
